;
(function($) {
    function border(el, style, width, color) {
        $(el).css({
            "border-style": style,
            "border-width": width,
            "border-color": color
        });
    }

    function size(el) {
        var $el = $(el);
        return {
            width: $el.width(),
            height: $el.height()
        };
    }

    /**
     * Use an iframe to load the content so we can have a DOM to manipulate to find elements.
     */

    function callIframe(url, callback) {
        // hidden iframe to load the content
        var f = $('<iframe width=1 height=1 style=display:none />');
        f.attr('src', url);
        $(document.body).append(f);
        var onReady = null;
        var onLoad = null;
        if ("function" === typeof callback) {
            onLoad = callback;
        } else if (callback) {
            onReady = callback.onReady;
            onLoad = callback.onLoad;
        }
        if (onLoad) {
            f.load(function() {
                onLoad(f[0]);
            });
        }
        if (onReady) {
            // Don't think this works correctly, so use onLoad only.
            f.ready(function() {
                onReady(f[0]);
            });
        }
    }

    function extractPeriodicTable(el) {
        var th = el.find('th:contains("Periodic table")').first();
        return th.closest("table").remove();
    }

    function alterElementLinks(periodicTable, onClick) {
        window.grimbo.onClick = function(idx) {
            var a = periodicTable.find("#grimbo-link-" + idx).first();
            onClick.call(a);
        };
        // kill links
        periodicTable.find("a").each(function(idx) {
            var $this = $(this);
            $this.attr("id", "grimbo-link-" + idx);
            $this.attr("data-href", $this.attr("href"));
            $this.attr("href", "javascript:grimbo.onClick(" + idx + ");");
        });
    }

    function ensurePeriodicTablePage() {
        var href = window.location.href;
        var home = "http://en.wikipedia.org/wiki/Periodic_table";
        if (href !== home) {
            window.location.href = home;
            return false;
        }
        return true;
    }

    function styleThePage() {
        var $head = $(document.head);
        // add the first style (highest precedence?) to be our custom styles
        $head.prepend($("<style>table.grimbo-infobox { margin: 0; padding: 0; }</style>"));
        var $body = $(document.body);
        $body.css("font-size", "10pt");
    }

    function preparePeriodicTable(periodicTable, infoboxContainer) {
        // prevent the [hide] button from being added each time an iframe is loaded
        // this is triggered by the table having a class of 'collapsible', so we clear the class.
        periodicTable.attr("class", "");
        // suggest to the table that it occupies the least space it can
        periodicTable.attr("width", "1px");
        periodicTable.css("width", "1px");
        periodicTable.css("float", "left");
        // truncate all larger padding to 1px
        periodicTable.find("th,td").css("padding", "1px");

        alterElementLinks(periodicTable, function(f) {
            var $this = $(this);
            var p = $this.parent();
            var isElementLink = (p && p.length > 0 && "TD" === p[0].tagName);
            if (!isElementLink) {
                return;
            }
            var href = $this.attr("data-href");
            console.log(this, $this, href);
            var time = new Date().getTime();
            // TODO - should there really be a reference to body here?
            infoboxContainer.setMaxSize($(document.body).width() - periodicTable.width(), $(document.body).height());
            infoboxContainer.load(href);
        });
    }

    /**
     * Class to load an element page from wikipedia.
     */

    function ElementLoader(container) {
        this.infoboxTemplate = null;
    }

    /**
     * Load an element page from wikipedia.
     * 
     * @return $.Deferred
     *            A deferred that will be resolved with the infobox of the requested element.
     */
    ElementLoader.prototype.load = function(url) {
        var dfd = $.Deferred();

        callIframe(url, function(iframe) {
            var f = $(iframe);
            var infobox = f.contents().find("table.infobox").first();
            infobox.remove();
            f.remove();
            dfd.resolveWith(undefined, [infobox]);
        });

        return dfd.promise();
    };

    function InfoboxContainer(el, elementLoader) {
        this.el = el;
        this.elementLoader = elementLoader;
        this.maxSize = null;
    }

    InfoboxContainer.prototype.setMaxSize = function(w, h) {
        this.maxSize = {
            width: w,
            height: h
        };
    };

    InfoboxContainer.prototype.setInfobox = function(infobox) {
        var el = this.el;

        // do this before clone so we can stack these tables next to each other
        infobox.css("clear", "none");
        // add a class to recognise the original and clones
        infobox.addClass("grimbo-infobox");

        // measure
        el.html("");
        el.append(infobox);
        var infoboxSize = size(infobox);

        var infoboxTemplate = this.infoboxTemplate || this.createInfoboxTemplate(infobox);
        console.log(infoboxSize, this.maxSize, infoboxTemplate);
        this.tryMultiColumn(infobox, infoboxTemplate, this.maxSize.width, this.maxSize.height);
    };

    InfoboxContainer.prototype.load = function(url) {
        this.el.html("Loading " + url);
        this.elementLoader.load(url).done(this.setInfobox.bind(this));
    };

    InfoboxContainer.prototype.createInfoboxTemplate = function(infobox) {
        // remove all the child elements and then grab the outerHTML.
        // this means we are left with the outer 'skeleton' html.
        var children = infobox.children();
        var w = infobox.width();
        infobox.attr("width", w);
        children.remove();
        var outer = infobox[0].outerHTML;
        infobox.append(children);
        return $(outer);
    };

    InfoboxContainer.prototype.tryMultiColumn = function(infobox, infoboxTemplate, maxWidth, maxHeight) {
        function shouldSplitOnHeight(tr) {
            var $tr = $(tr);
            var trOff = $tr.offset();
            var trSize = size($tr);
            console.log("tr", tr, $tr.children("th").text(), "trOff.top", trOff.top, "trSize.height", trSize.height, "top+height", (trOff.top + trSize.height), $tr.height(), $tr.outerHeight());
            return (trOff.top + trSize.height >= maxHeight);
        }
        function shouldSplit(tr) {
            return shouldSplitOnHeight(tr);
        }
        var trs = null;
        var trimmed = true;
        var iw = infobox.width();
        var availableWidth = maxWidth - iw;
        while (trimmed && availableWidth > iw) {
            console.log("maxWidth", maxWidth, "maxHeight", maxHeight);
            trs = infobox.children("tbody").children("tr");
            if (!(trs && trs.length > 0)) {
                break;
            }
            console.log(trs.length);
            trimmed = false;
            trs.each(function() {
                console.log(this);
                if (shouldSplit(this)) {
                    console.log("too high");
                    var prev = $(this).prevAll();
                    console.log(prev);
                    var prev = [].reverse.call(prev);
                    console.log(prev);
                    var newinfobox = infoboxTemplate.clone().append(prev);
                    infobox.after(newinfobox);
                    // assumes iw is all the width that is taken up, no padding etc
                    availableWidth -= iw;
                    trimmed = true;
                    return false;
                }
            });
            console.log(trimmed);
        }
    };

    function onLoad() {
        if (!ensurePeriodicTablePage()) {
            return;
        }

        // make a namespace
        window.grimbo = window.grimbo || {};

        styleThePage();

        var $body = $(document.body);
        var periodicTable = extractPeriodicTable($body);
        $body.html("");

        var bodySize = size($body);

        var elementLoader = new ElementLoader();
        var div = $("<div/>");
        var infoboxContainer = new InfoboxContainer(div, elementLoader);

        preparePeriodicTable(periodicTable, infoboxContainer);

        $body.append(periodicTable);
        $body.append(div);

        infoboxContainer.setMaxSize(bodySize.width - periodicTable.width(), bodySize.height);
        infoboxContainer.load("http://en.wikipedia.org/wiki/Hydrogen");
    }

    onLoad();
}(jQuery));